package com.zmj.sy.mom.srv.aps.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zmj.sy.mom.srv.aps.bean.entity.base.BaseEntity;
import com.zmj.sy.mom.srv.aps.bean.entity.order.Order;
import com.zmj.sy.mom.srv.aps.bean.entity.order.OrderAccessories;
import com.zmj.sy.mom.srv.aps.bean.entity.order.OrderBom;
import com.zmj.sy.mom.srv.aps.bean.entity.order.OrderPhase;
import com.zmj.sy.mom.srv.aps.bean.entity.pre.PreOrderWelding;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BaseIdReqVo;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BaseListResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.base.BasePageResVo;
import com.zmj.sy.mom.srv.aps.bean.vo.planorder.*;
import com.zmj.sy.mom.srv.aps.mapper.*;
import com.zmj.sy.mom.srv.aps.utils.JsonUtils;
import com.zmj.sy.mom.srv.aps.utils.OrderUtils;
import com.zmj.sy.mom.srv.aps.utils.SyExceptionUtils;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.io.File;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@RequiredArgsConstructor
@Service
public class PlanOrderService {

    private final OrderMapper orderMapper;
    private final PreOrderWeldingMapper preOrderWeldingMapper;
    private final ObjectMapper objectMapper;

    private static String prePath = "/pre";
    private static String specStr = ",";
    private final OrderBomMapper orderBomMapper;
    private final OrderPhaseMapper orderPhaseMapper;
    private final OrderAccessoriesMapper orderAccessoriesMapper;

    @SneakyThrows
    @Transactional
    public void add(PlanOrderWeldingAddReqVo reqVo) {


        List<Order> orderList = orderMapper.selectBatchIds(reqVo.getIds());
        if(CollUtil.isEmpty(orderList)){
            throw SyExceptionUtils.e("没有找到该订单");
        }

        List<OrderBom> orderBomList = orderBomMapper.lambdaQuery()
                .in(OrderBom::getOrderId, orderList.stream().map(BaseEntity::getId).distinct().collect(Collectors.toList()))
                .eq(OrderBom::getMaterType, "Assembly")
                .list();

        String constructCodes = orderList.stream().map(Order::getConstructNo).filter(Objects::nonNull).distinct().sorted().collect(Collectors.joining(specStr));

        orderBomList = orderBomList.stream()
                .filter(e-> e.getPlmId().matches(".+01P([1-6]|DB)(_170)?$"))
                .collect(Collectors.toList());

        List<String> orderType = Arrays.asList("D", "Y", "Z");
        Map<String, Integer> params = new HashMap<>();
        for (Order order : orderList) {

            if(!order.getOrderType().equals(2)){
               throw SyExceptionUtils.e("非MBD导入的订单无法统计焊接平衡");
            }

            String suffix = OrderUtils.partType(order.getPlmId(), order.getMaterName());
            Integer put = params.put(suffix, 1);
            if (!orderType.contains(suffix)) {
                throw SyExceptionUtils.e("不是三大件");
            }
            if(put != null){
                throw SyExceptionUtils.e(order.getMaterName().replaceAll("[一二三四五六七]拼$", "") + "重复");
            }
        }

        Map<String, Integer> hanbiMap = new HashMap<>();
        hanbiMap.put("D", 34);
        hanbiMap.put("Y", 30);
        hanbiMap.put("Z", 24);

        Map<String, Integer> orderSortMap = new HashMap<>();
        orderSortMap.put("D", 1);
        orderSortMap.put("Y", 2);
        orderSortMap.put("Z", 3);

        Map<String, Integer> phaseSortMap = new HashMap<>();
        phaseSortMap.put("BH", 1);
        phaseSortMap.put("PH", 2);
        phaseSortMap.put("LH", 3);
        phaseSortMap.put("RH", 4);

        List<String> phaseList = new ArrayList<>(phaseSortMap.keySet());
        phaseList.sort(Comparator.comparingInt(phaseSortMap::get));

        List<OrderPhase> orderPhaseList = orderPhaseMapper.lambdaQuery()
                .in(OrderPhase::getBomId, orderBomList.stream().map(BaseEntity::getId).distinct().collect(Collectors.toList()))
                .in(OrderPhase::getPhaseCode, new ArrayList<>(phaseSortMap.keySet()))
                .list();

        List<Integer> phaseIds = orderPhaseList.stream().map(BaseEntity::getId).collect(Collectors.toList());
        List<OrderAccessories> list = orderAccessoriesMapper.lambdaQuery().in(OrderAccessories::getPhaseId, phaseIds).list();
        Map<Integer, OrderAccessories> phaseAccessoriesMap = list.stream().collect(Collectors.toMap(OrderAccessories::getPhaseId, Function.identity()));

        // 跨别信息
        List<PlanOrderWeldingDetailSpanResVo> orderListEntity = orderList.stream()
                .map(e -> {
                    PlanOrderWeldingDetailSpanResVo span = new PlanOrderWeldingDetailSpanResVo();

                    if(e.getPlmId().contains("0101P")){
                        span.setSpanName("顶梁");
                    } else if(e.getPlmId().contains("0201P")){
                        span.setSpanName("掩护梁");
                    } else if(e.getPlmId().contains("0301P")){
                        span.setSpanName("底座");
                    } else {
                        throw SyExceptionUtils.e("未知的类型");
                    }

                    String suffix = OrderUtils.partType(e.getPlmId(), e.getMaterName());
                    span.setWeldingRobotArm(hanbiMap.get(suffix));
                    span.setOrderId(e.getId());
                    span.setSuffix(suffix);
                    return span;
                })
                .collect(Collectors.toList());

        Map<Integer, PlanOrderWeldingDetailSpanResVo> orderIdMap = orderListEntity.stream().collect(Collectors.toMap(PlanOrderWeldingDetailSpanResVo::getOrderId, Function.identity()));

        // 工序信息
        Map<Integer, OrderBom> orderBomMap = orderBomList.stream().collect(Collectors.toMap(BaseEntity::getId, Function.identity()));
        List<PlanOrderWeldingDetailPhaseResVo> phaseListEntity = orderPhaseList.stream()
                .sorted((o1, o2) -> {
                    String o1Suffix = orderIdMap.get(o1.getOrderId()).getSuffix();
                    String o2Suffix = orderIdMap.get(o2.getOrderId()).getSuffix();
                    int i = orderSortMap.get(o1Suffix).compareTo(orderSortMap.get(o2Suffix));
                    if (i != 0) {
                        return i;
                    }
                    Integer o1Level = orderBomMap.get(o1.getBomId()).getLevel();
                    Integer o2Level = orderBomMap.get(o2.getBomId()).getLevel();
                    i = o1Level.compareTo(o2Level);
                    if (i != 0) {
                        return -i;
                    }

                    return phaseSortMap.get(o1.getPhaseCode()).compareTo(phaseSortMap.get(o2.getPhaseCode()));

                })
                .map(e -> {
                    PlanOrderWeldingDetailPhaseResVo phase = new PlanOrderWeldingDetailPhaseResVo();
                    phase.setPhaseId(e.getId());
                    phase.setOrderId(e.getOrderId());
                    phase.setPhaseCode(e.getPhaseCode());
                    OrderBom ob = orderBomMap.get(e.getBomId());
                    phase.setMaterName(ob.getMaterName() + e.getPhaseName());

                    phase.setCapabilities("5");
                    OrderAccessories orderAccessories = phaseAccessoriesMap.get(phase.getPhaseId());
                    if (orderAccessories == null) {
                        phase.setWireModel("/");
                        phase.setWireTotal("/");
                    } else {
                        phase.setWireModel(orderAccessories.getSpec());
                        phase.setWireTotal(orderAccessories.getPartCount().toPlainString());
                    }
                    phase.setDistribution("/");
                    phase.setProductionNum("/");
                    phase.setReadOnly(1);
                    phase.setHours(24);
                    return phase;
                })
                .collect(Collectors.toList());

        List<String> phList = Arrays.asList("PH", "BH", "LH");

        Map<Integer, List<PlanOrderWeldingDetailPhaseResVo>> orderPhaseMap = phaseListEntity.stream().collect(Collectors.groupingBy(PlanOrderWeldingDetailPhaseResVo::getOrderId));
        for (PlanOrderWeldingDetailSpanResVo order : orderListEntity) {
            List<PlanOrderWeldingDetailPhaseResVo> phaseOrderList = orderPhaseMap.get(order.getOrderId());

            Integer weldingRobotArm = order.getWeldingRobotArm();
            BigDecimal result = phaseOrderList.stream().filter(e -> phList.contains(e.getPhaseCode())).map(e -> new BigDecimal(e.getWireTotal())).reduce(BigDecimal.ZERO, BigDecimal::add);
            for (PlanOrderWeldingDetailPhaseResVo phase : phaseOrderList) {
                if(phase.getPhaseCode().equals("RH")){
                    phase.setDistribution("/");
                    phase.setProductionNum("/");
                } else {
                    BigDecimal distribution = new BigDecimal(phase.getWireTotal()).divide(result, 8, RoundingMode.HALF_UP).multiply(new BigDecimal(weldingRobotArm)).setScale(0, RoundingMode.DOWN);
                    phase.setDistribution(distribution.toPlainString());

                    BigDecimal productionNum = distribution.multiply(new BigDecimal(phase.getCapabilities()).multiply(new BigDecimal(phase.getHours()))).divide(new BigDecimal(phase.getWireTotal()), 0, RoundingMode.DOWN);
                    phase.setProductionNum(productionNum.toPlainString());
                }
            }
        }

        PlanOrderWeldingDetailResVo resVo = new PlanOrderWeldingDetailResVo();

        resVo.setTitle(constructCodes);
        resVo.setOrderList(orderListEntity);
        resVo.setPhaseList(phaseListEntity);

        PreOrderWelding pow = new PreOrderWelding();
        String filePath =  "/" + UUID.randomUUID() + ".json";
        pow.setPath(filePath);
        String json = JsonUtils.toJsonString(resVo);
        FileUtils.writeStringToFile(new File(prePath + filePath), json, "utf-8");

        pow.setConstructCodes(constructCodes);
        pow.setOrderIds( orderList.stream().map(BaseEntity::getId).map(String::valueOf).distinct().collect(Collectors.joining(specStr)));
        preOrderWeldingMapper.insert(pow);

    }

    @Transactional(readOnly = true)
    public BasePageResVo<PlanOrderWeldingListResVo> list(PlanOrderWeldingListReqVo reqVo) {
        Page<PreOrderWelding> page = preOrderWeldingMapper.lambdaQuery()
                .like(StringUtils.hasText(reqVo.getConstructCodes()), PreOrderWelding::getConstructCodes, reqVo.getConstructCodes())
                .like(StringUtils.hasText(reqVo.getCreateUsername()), PreOrderWelding::getCreateUsername, reqVo.getCreateUsername())
                .orderByDesc(PreOrderWelding::getId)
                .page(reqVo.toPage());
        return BasePageResVo.of(page, PlanOrderWeldingListResVo.class);
    }

    @SneakyThrows
    @Transactional(readOnly = true)
    public PlanOrderWeldingDetailResVo detail(BaseIdReqVo reqVo) {
        PreOrderWelding preOrderWelding = preOrderWeldingMapper.selectById(reqVo.getId());
        String jsonStr = FileUtils.readFileToString(new File(prePath +preOrderWelding.getPath()), "utf-8");

        PlanOrderWeldingDetailResVo planOrderWeldingDetailResVo = objectMapper.readValue(jsonStr, PlanOrderWeldingDetailResVo.class);
        for (PlanOrderWeldingDetailPhaseResVo planOrderWeldingDetailPhaseResVo : planOrderWeldingDetailResVo.getPhaseList()) {
            if(planOrderWeldingDetailPhaseResVo.getHours() == null){
                planOrderWeldingDetailPhaseResVo.setHours(24);
            }
        }
        return objectMapper.readValue(jsonStr, PlanOrderWeldingDetailResVo.class);
    }

    @SneakyThrows
    @Transactional
    public void edit(PlanOrderWeldingEditReqVo reqVo) {
        PreOrderWelding preOrderWelding = preOrderWeldingMapper.selectById(reqVo.getId());

        PlanOrderWeldingDetailResVo rr = new PlanOrderWeldingDetailResVo();
        rr.setTitle(reqVo.getTitle());
        rr.setOrderList(reqVo.getOrderList());
        rr.setPhaseList(reqVo.getPhaseList());

        updateAllProductionNum(rr);

        String filePath =  "/" + UUID.randomUUID() + ".json";
        preOrderWelding.setPath(filePath);
        String json = JsonUtils.toJsonString(rr);
        FileUtils.writeStringToFile(new File(prePath +filePath), json, "utf-8");

        preOrderWeldingMapper.updateById(preOrderWelding);
    }

    @SneakyThrows
    @Transactional(readOnly = true)
    public BaseListResVo<PlanOrderWeldingSelectResVo> select(PlanOrderWeldingSelectReqVo reqVo) {

        PreOrderWelding preOrderWelding = preOrderWeldingMapper.selectById(reqVo.getId());
        String jsonStr = FileUtils.readFileToString(new File(prePath + preOrderWelding.getPath()), "utf-8");
        PlanOrderWeldingDetailResVo root = objectMapper.readValue(jsonStr, PlanOrderWeldingDetailResVo.class);


        List<PlanOrderWeldingSelectResVo> collect = root.getPhaseList()
                .stream()
                .filter(e -> !Objects.equals(e.getOrderId(), reqVo.getOrderId()) && e.getReadOnly().equals(1))
                .map(e -> {
                    PlanOrderWeldingSelectResVo resVo = new PlanOrderWeldingSelectResVo();
                    resVo.setName(e.getMaterName());
                    resVo.setPhaseId(e.getPhaseId());
                    return resVo;
                })
                .collect(Collectors.toList());
        return new BaseListResVo<>(collect);
    }

    @Transactional
    @SneakyThrows
    public void addColumn(PlanOrderWeldingAddColumnReqVo reqVo) {
        PreOrderWelding preOrderWelding = preOrderWeldingMapper.selectById(reqVo.getId());
        String jsonStr = FileUtils.readFileToString(new File(prePath + preOrderWelding.getPath()), "utf-8");
        PlanOrderWeldingDetailResVo root = objectMapper.readValue(jsonStr, PlanOrderWeldingDetailResVo.class);
        if(CollUtil.isEmpty(root.getPhaseList())){
            throw SyExceptionUtils.e("没啥工序");
        }

        Optional<PlanOrderWeldingDetailPhaseResVo> minOps = root.getPhaseList().stream().min(Comparator.comparing(PlanOrderWeldingDetailPhaseResVo::getPhaseId));

        if(!minOps.isPresent()){
            return ;
        }

        Integer minPhaseId = minOps.get().getPhaseId();
        if(minPhaseId == 1){
            minPhaseId = 0;
        }

        Map<Integer, PlanOrderWeldingDetailPhaseResVo> phaseMap = root.getPhaseList().stream().collect(Collectors.toMap(PlanOrderWeldingDetailPhaseResVo::getPhaseId, Function.identity()));

        PlanOrderWeldingDetailPhaseResVo source = phaseMap.get(reqVo.getPhaseId());
        PlanOrderWeldingDetailPhaseResVo newPhase = new PlanOrderWeldingDetailPhaseResVo();
        BeanUtils.copyProperties(source, newPhase);
        newPhase.setPhaseId(minPhaseId - 1);
        newPhase.setOrderId(reqVo.getOrderId());
        newPhase.setReadOnly(2);


        if (root.getPhaseList().stream().anyMatch(e -> e.getOrderId().equals(newPhase.getOrderId()) && e.getMaterName().equals(newPhase.getMaterName()))) {
            throw SyExceptionUtils.e("相同工序不得重复添加!");
        }

        root.getPhaseList().add(newPhase);
//        root.setHours(reqVo.getHours());

        updateAllProductionNum(root);

        String filePath = "/" + UUID.randomUUID() + ".json";
        String json = JsonUtils.toJsonString(root);
        FileUtils.writeStringToFile(new File(prePath + filePath), json, "utf-8");
        preOrderWelding.setPath(filePath);
        preOrderWeldingMapper.updateById(preOrderWelding);
    }

    @Transactional
    @SneakyThrows
    public void delColumn(PlanOrderWeldingDelColumnReqVo reqVo) {
        PreOrderWelding preOrderWelding = preOrderWeldingMapper.selectById(reqVo.getId());
        String jsonStr = FileUtils.readFileToString(new File(prePath +preOrderWelding.getPath()), "utf-8");
        PlanOrderWeldingDetailResVo root = objectMapper.readValue(jsonStr, PlanOrderWeldingDetailResVo.class);

        root.getPhaseList().removeIf(e -> e.getReadOnly() == 2 && e.getPhaseId().equals(reqVo.getPhaseId()));
//        root.setHours(reqVo.getHours());

        updateAllProductionNum(root);

        String filePath = "/" + UUID.randomUUID() + ".json";
        String json = JsonUtils.toJsonString(root);
        FileUtils.writeStringToFile(new File(prePath + filePath), json, "utf-8");
        preOrderWelding.setPath(filePath);
        preOrderWeldingMapper.updateById(preOrderWelding);
    }

    public void updateAllProductionNum(PlanOrderWeldingDetailResVo reqVo) {

        Map<Integer, List<PlanOrderWeldingDetailPhaseResVo>> orderPhaseMap = reqVo.getPhaseList().stream().collect(Collectors.groupingBy(PlanOrderWeldingDetailPhaseResVo::getOrderId));
        for (PlanOrderWeldingDetailSpanResVo order : reqVo.getOrderList()) {
            List<PlanOrderWeldingDetailPhaseResVo> phaseOrderList = orderPhaseMap.get(order.getOrderId());
            for (PlanOrderWeldingDetailPhaseResVo phase : phaseOrderList) {
                if(phase.getHours() == null){
                    phase.setHours(24);
                }

                if(phase.getPhaseCode().equals("RH")){
                    phase.setProductionNum("/");
                } else {
                    BigDecimal productionNum = new BigDecimal(phase.getDistribution()).multiply(new BigDecimal(phase.getCapabilities()).multiply(new BigDecimal(phase.getHours()))).divide(new BigDecimal(phase.getWireTotal()), 0, RoundingMode.DOWN);
                    phase.setProductionNum(productionNum.toPlainString());
                }
            }
        }
    }
}
